﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Data.Common;
using System.Data.OleDb;
using System.Drawing;
using System.Text;
using System.Windows.Forms;

namespace Tilver.DBConnectionUI
{
    internal partial class frmDialog : Form
    {        
        bool providersLoaded = false;
        bool oleProvidersLoaded = false;
        OleDbProvider defaultOleDbProvider = new OleDbProvider("Microsoft.Jet.OLEDB.4.0", "OleDb provider for Jet 4.0");
        ConnectionStringDialog _dialog;
        DbProvider _activeProvider;
        OleDbProvider _activeOleProvider;
        IUDLEditor _activeEditor;

        public frmDialog(ConnectionStringDialog parentDialog)
        {
            InitializeComponent();
            _dialog = parentDialog;
        }
        private void frmDialog_Load(object sender, EventArgs e)
        {
            pages.Add(page0);
            pages.Add(page1);
            pages.Add(page2);
            pages.Add(page3);

            // first we try to load the requested provider
            // if fail then we'll show the list of providers
            if (!string.IsNullOrEmpty(_dialog.Provider) &&
                ConnectionStringDialog.Providers[_dialog.Provider] != null)
            {
                if (IsOleDb(_dialog.Provider))
                {
                    OleDbProvider oledb;
                    if (!string.IsNullOrEmpty(_dialog.ConnectionString))
                        oledb = ParseOleProvider(_dialog.ConnectionString);
                    else
                        oledb = defaultOleDbProvider;

                    _activeOleProvider = oledb;
                    txOleDbProvider.Text = _activeOleProvider.Description;
                }

                _activeProvider = ConnectionStringDialog.Providers[_dialog.Provider];
                txDataProvider.Text = _activeProvider.Description;
                LoadEditor();
                SetConnectionString(_dialog.ConnectionString);
                return;
            }
            else
            {
                // if there is only one provider available
                // no need to show he list.
                if (ConnectionStringDialog.Providers.AvailableProviders.Length == 1)
                {
                    _activeProvider = ConnectionStringDialog.Providers.AvailableProviders[0];
                    txDataProvider.Text = _activeProvider.Description;
                    LoadEditor();
                }
                else
                    ShowProvidersList();
            }
        }

        void UpdateProvider(string providerID)
        {
            if(string.IsNullOrEmpty(providerID) || 
               ConnectionStringDialog.Providers[providerID] == null)
                throw new InvalidOperationException("Invalid or null provider");
            
            _activeProvider = ConnectionStringDialog.Providers[providerID];

            if(IsOleDb(_activeProvider) && null == _activeOleProvider)
                _activeOleProvider = defaultOleDbProvider;

            LoadEditor();
        }
        void LoadEditor()
        {
            // up to this point we should have an activeProvider
            // if not something must be wrong
            if (_activeProvider == null || 
               (IsOleDb(_activeProvider) && 
               null == _activeOleProvider)) 
                    throw new InvalidOperationException("No current provider");
            
            IUDLEditor editor = CreateEditor();
            // helps a little with flickering
            this.SuspendLayout();

            // reset current data
            this.oleProviderTextPanel.Visible = IsOleDb(_activeProvider);
            this.EditorPanel.AutoSize = false;
            this.EditorPanel.Controls.Clear();

            // load new editor
            this._activeEditor = editor;
            EditorPanel.Controls.Add((Control)editor);
            EditorPanel.AutoSize = true;
            ((Control)editor).Focus();
            txDataProvider.Text = _activeProvider.Description;
            if (_activeOleProvider != null && editor is IOleDbEditor)
                ((IOleDbEditor)editor).SetOleProviderID(_activeOleProvider.InvariantName);

            editor.RefreshData();
            this.ResumeLayout();
            ShowPage(2);
        }
        void SetConnectionString(string connString)
        {
            if (!string.IsNullOrEmpty(connString))
                _activeEditor.ConnectionString = connString;
        }

        IUDLEditor CreateEditor()
        {
            IUDLEditor editor = null;
            if (IsOleDb(_activeProvider))
            {
                editor = ConnectionStringDialog.Editors[_activeOleProvider.InvariantName];
                if (editor == null)
                    editor = ConnectionStringDialog.Editors["System.Data.OleDb"];
                //editor.SetValue("Provider", _activeOleProvider.InvariantName);
                txOleDbProvider.Text = _activeOleProvider.Description;
            }
            else
                editor = ConnectionStringDialog.Editors[_activeProvider.InvariantName];

            if (editor == null)
                editor = ConnectionStringDialog.Editors["generic"];
            return editor;
        }
        void ShowProvidersList()
        {
            if (!providersLoaded)
            {
                // load list of available providers
                foreach (DbProvider provider in ConnectionStringDialog.Providers.AvailableProviders)
                {
                    LinkLabel lbl = new LinkLabel();
                    lbl.AutoEllipsis = true;
                    lbl.AutoSize = true;
                    lbl.Padding = new Padding(4);
                    lbl.Text = provider.ToString();
                    lbl.Tag = provider;
                    lbl.LinkClicked += delegate(object sender, LinkLabelLinkClickedEventArgs e)
                    {
                        DbProvider prov = (DbProvider)((LinkLabel)sender).Tag;
                        _activeOleProvider = null;
                        UpdateProvider(prov.InvariantName);
                    };
                    providersList.Controls.Add(lbl);
                }
                providersLoaded = true;
            }
            ShowPage(0);
        }
        void ShowOleProvidersList()
        {
            if (!oleProvidersLoaded)
            {
                Cursor = Cursors.WaitCursor;

                oleProvidersList.Sorted = true;
                using (OleDbDataReader reader = OleDbEnumerator.GetRootEnumerator())
                {
                    while (reader.Read())
                    {
                        if (reader.GetInt32(3) == 1)
                        {
                            string id = reader.GetString(0);
                            string desc = reader.GetString(2);
                            //filter out OleDb ODBC provider not supported on .NET
                            if(!id.Contains("MSDASQL"))
                                oleProvidersList.Items.Add(new OleDbProvider(reader.GetString(0), reader.GetString(2)));
                        }
                    }
                }
                oleProvidersList.DoubleClick += delegate
                {
                    if (oleProvidersList.SelectedItem is OleDbProvider)
                    {
                        OleDbProvider prov = (OleDbProvider)oleProvidersList.SelectedItem;
                        this._activeOleProvider = prov;
                        UpdateProvider(_activeProvider.InvariantName);
                    }
                };
                oleProvidersLoaded = true;

                Cursor = Cursors.Default;
            }
            ShowPage(1);
        }
        void ShowAdvancedProperties()
        {
            if (_activeEditor != null)
            {
                listBox1.SelectedObject = _activeEditor.GetAdvancedPropertiesObject();
                ShowPage(3);
            }
        }

        bool IsOleDb(DbProvider provider)
        {
            return provider != null && provider.InvariantName.Equals("System.Data.OleDb");
        }
        bool IsOleDb(string provider)
        {
            return provider.Equals("System.Data.OleDb");
        }
        OleDbProvider ParseOleProvider(string connString)
        {
            DbConnectionStringBuilder tmp = new DbConnectionStringBuilder();
            tmp.ConnectionString = connString;
            if (tmp.ContainsKey("Provider"))
                return new OleDbProvider(tmp["Provider"].ToString(),tmp["Provider"].ToString());
            return null;
        }

        private void cmdTest_Click(object sender, EventArgs e)
        {
            if (_activeProvider != null && _activeEditor != null)
            {
                string msg;
                _activeProvider.TestConnection(_activeEditor.ConnectionString, out msg);
                MessageBox.Show(msg,"Test complete");
            }
        }

        private void cmdAdvanced_Click(object sender, EventArgs e)
        {
            ShowAdvancedProperties();
        }

        private void cmdChangeProvider_Click(object sender, EventArgs e)
        {
            ShowProvidersList();
        }

        private void cmdChangeOleDbProvider_Click(object sender, EventArgs e)
        {
            ShowOleProvidersList();
        }

        private void cmdOK_Click(object sender, EventArgs e)
        {
            if (_dialog.ValidateConnections)
            {
                if (_activeProvider == null || _activeEditor == null)
                {
                    MessageBox.Show("Incomplete connection string", "Validation failed");
                    return;
                }

                string msg;
                if (!_activeProvider.TestConnection(_activeEditor.ConnectionString, out msg))
                {
                    MessageBox.Show(msg, "Validation failed");
                    return;
                }
            }
            _dialog.Provider = _activeProvider.InvariantName;
            _dialog.ConnectionString = _activeEditor.ConnectionString;
            this.DialogResult = DialogResult.OK;
        }

        private void cmdBack_Click(object sender, EventArgs e)
        {
            ShowPage(2);
        }

        #region paging
        List<Control> pages = new List<Control>();
        void ShowPage(int index)
        {
            this.SuspendLayout();
            foreach (Control ctl in pages)
                ctl.Visible = false;

            Control c = pages[index];
            c.Location = new Point(4, 0);
            commandsContainer.Location = new Point(4, c.Bottom + 2);
            commandsContainer.Width = c.Width - 8;

            this.AutoSizeMode = AutoSizeMode.GrowAndShrink;
            this.AutoSize = true;
            c.Visible = true;

            cmdTest.Enabled = index == 2;
            cmdOK.Enabled = index == 2;
            
            this.ResumeLayout();
        }
        #endregion


    }
}
